package org.pc2r.game.play;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.pc2r.game.Game;
import org.pc2r.game.GameManager;
import org.pc2r.game.model.Player;
import org.pc2r.game.model.SubMarine;
import org.pc2r.protocole.EnumRequest;
import org.pc2r.protocole.RequestBuilder;
import org.pc2r.protocole.message.DeathWinnerMessage;
import org.pc2r.protocole.message.PositionMessage;
import org.pc2r.protocole.message.YourTurnMessage;

public class OneTurn implements Callable<Boolean> {

	private Logger logger;
	private GameManager game;

	public OneTurn(GameManager game, Logger logger) {
		this.game = game;
		this.logger = logger;
	}

	@Override
	public Boolean call() {
		Game game2 = game.getGame();
		List<Player> sortedPlayers = game2.getSortedPlayers();
		for (Player player : sortedPlayers) {
			// Si le joueur est mort, on le skip.
			
			if (player.isDead()) {
				logger.info("Le joueur " + player + " est mort. On passe son tour.");
				continue;
			}
			
			// On envoie un message YOURTURN au joueur qui doit jouer
			YourTurnMessage message = new YourTurnMessage(player);
			String name = player.getName();
			String request = null;
			try {
				logger.info("Envoi du YOURTURN à " + player);
				game2.send(name, message);
				// On attend la réponse du client, un ACTION
				request = game2.read(name);
				logger.info("Réception de " + player + " du message " + request);
			} catch (IOException e) {
				logger.log(Level.SEVERE, "Le joueur " + player
						+ " a été déconnecté.", e);
				// Si le joueur déconnecté rend la partie invalide, on la kill
				if (game.removeAndStop(name)) {
				  return Boolean.FALSE;
				}
				return Boolean.TRUE;
			}
			String[] action = RequestBuilder.parseAction(request,
					player.getAction());
			for (String act : action) {
				char charAt = act.charAt(0);
				if (charAt == 'E') {
					// Si des joueurs ont été déco, on vérifie si on doit stopper la partie ou pas.
					if(fire(game2, player, sortedPlayers)) {
					  return Boolean.FALSE;
					}
				} else {
					// On déplace le drone
					player.move(charAt);
				}
			}
		}
		return Boolean.TRUE;
	}
	
	private boolean fire(Game game, Player player, List<Player> sortedList) {
		List<SubMarine> fire = player.fire(game);
		// On a rien touché, on envoie un miss
		String name = player.getName();
		if(fire.isEmpty()) {
			try {
				game.send(name, new PositionMessage(EnumRequest.MISS, player.getDrone()));
			} catch (IOException e) {
				logger.log(Level.SEVERE, "Le joueur " + player + " a été déconnecté.", e);
				if(this.game.removeAndStop(name)) {
					return true;
				}
			}
		} else {
			// On a touché au moins 1 sous marin, on envoie un TOUCH au joueur car il a touché un sous marin
			try {
				game.send(name, new PositionMessage(EnumRequest.TOUCH, player.getDrone()));
			} catch (IOException e) {
				logger.log(Level.SEVERE, "Le joueur " + player + " a été déconnecté.", e);
				if(this.game.removeAndStop(name)) {
					return true;
				}
			}
			// On enregistre tous les joueurs qui ont été touchés
			Set<Player> touchedPlayers = new HashSet<>(4);
			for(SubMarine sub : fire) {
				touchedPlayers.add(sub.getPlayer());
			}
			// Pour tous les joueurs touchés, on envoie un OUCH
			for(Player touch : touchedPlayers) {
				try {
					game.send(touch.getName(), new PositionMessage(EnumRequest.OUCH, player.getDrone()));
				} catch (IOException e) {
					logger.log(Level.SEVERE, "Le joueur " + touch + " a été déconnecté.", e);
					if(this.game.removeAndStop(touch.getName())) {
						return true;
					}
				}
				// On enlève les sous-marins détruits
				touch.deleteDeadSubmarines();
			}
			// On regarde si les joueurs sont morts. 
			for(Player touch : touchedPlayers) {
			  if(touch.isDead()) {
			    // On envoie un DEATH à tout le monde pour chaque joueur mort
			    List<String> broadcast = game.broadcast(new DeathWinnerMessage(EnumRequest.DEATH, touch.getName()));
			    for(String disconnected : broadcast) {
			      // On stoppe la partie si plus assez de joueurs
			      if(this.game.removeAndStop(disconnected)) {
			        return true;
			      }
			    }
			  }
			}
		}		
		return false;
	}

}
